﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace PrisonStep
{
    public class PieFire
    {

        #region Member Variables
        /// <summary>
        /// Our game
        /// </summary>
        private PrisonGame mGame;

        /// <summary>
        /// The pie model
        /// </summary>
        private Model mPieModel;

        private List<PieBlast> mPieBlasts = new List<PieBlast>();

        private List<List<ModelMesh>> mPieModels = new List<List<ModelMesh>>();
        /// <summary>
        /// How many shots we have left
        /// </summary>
        private int mBulletsLeft = 10;

        /// <summary>
        /// How many pies we will have when we return to section 1.
        /// </summary>
        private const int mReloadedBullets = 10;

        /// <summary>
        /// Which Pie we will fire next to be updated in the fire function
        /// </summary>
        private int mCurrentPie = 0;

        private PieBlast mGortPie = null;

        private PieBlast mAlienPie = null;

        private PieBlast mDalekPie = null;


        #endregion

        #region Properties

        //public LinkedList<PieBlast> PieBlasts { get { return mPieBlasts; } }

        public PieBlast GortPie { get { return mGortPie; } set { mGortPie = value; } }

        public PieBlast AlienPie { get { return mAlienPie; } set { mAlienPie = value; } }

        public PieBlast DalekPie { get { return mDalekPie; } set { mDalekPie = value; } }

        #endregion

        // Only need to do this if all the pies are present in the bazooka when
        public class PieBlast
        {
            public enum States { Loaded, FiredStillInBarrel, FiredLeftBarrel, Collision, Attached }
            public Matrix orientation = Matrix.Identity;
            public float mSpeed = 300;
            public bool mWallHit = false;
            public int mPieNumber;          // Which pie number out of the three
            public int mSection = 1;        
            public List<ModelMesh> mMesh;
            public States mState = States.Loaded;
            public float mElapsedTime = 0;  //This is used to determine when the pie is outside the barrel of the bazooka, when elapsed time * speed is greater than 91.5
        }

        /// <summary>
        /// The constructor
        /// </summary>
        /// <param name="aGame"></param>
        public PieFire(PrisonGame aGame)
        {
            this.mGame = aGame;
        }

        /// <summary>
        /// This function is called to load content into this component
        /// of our game
        /// </summary>
        /// <param name="aContent"></param>
        public void LoadContent(ContentManager aContent)
        {
            mPieModel = aContent.Load<Model>("pies");

            List<ModelMesh> lPie1 = new List<ModelMesh>();
            List<ModelMesh> lPie2 = new List<ModelMesh>();
            List<ModelMesh> lPie3 = new List<ModelMesh>();

            foreach (ModelMesh lMesh in mPieModel.Meshes)
            {
                if (lMesh.Name.StartsWith("Pie1"))
                    lPie1.Add(lMesh);
                else if (lMesh.Name.StartsWith("Pie2"))
                    lPie2.Add(lMesh);
                else if (lMesh.Name.StartsWith("Pie3"))
                    lPie3.Add(lMesh);
            }

            mPieModels.Add(lPie1);
            mPieModels.Add(lPie2);
            mPieModels.Add(lPie3);

            PieBlast lPie = new PieBlast();
            lPie.orientation = mGame.Victoria.BazMat;
            lPie.mMesh = mPieModels[mCurrentPie++];
            mPieBlasts.Add(lPie);
            

        }

        /// <summary>
        /// This function is called to update this component of our game
        /// to the current game time
        /// </summary>
        /// <param name="aGameTime"></param>
        public void Update(GameTime aGameTime)
        {
            float lDelta = (float)aGameTime.ElapsedGameTime.TotalSeconds;


            for (int i = 0; i < mPieBlasts.Count; i++)
            {
                PieBlast lPieBlast = mPieBlasts[i];

                Matrix lNewOrientation;
                switch (lPieBlast.mState)
                {
                    case PieBlast.States.Loaded:
                        lPieBlast.orientation = mGame.Victoria.BazMat;
                        break;

                    // The Pie in question has been fired but is still inside the barrel of the bazooka
                    case PieBlast.States.FiredStillInBarrel:
                        lPieBlast.mElapsedTime += lDelta;
                        lPieBlast.orientation = mGame.Victoria.BazMat * Matrix.CreateTranslation(mGame.Victoria.BazMat.Backward * lPieBlast.mSpeed * lPieBlast.mElapsedTime);

                        // Since BazMat.Backwards is a unit vector, when lPieBlast.mSpeed * lPieBlast.mElapsedTime
                        // is greater than 91.5 the pie has travelled out of the barrel and can be transitioned to
                        // the FiredLeftBarrel State.
                        
                        if ((lPieBlast.mSpeed * lPieBlast.mElapsedTime) > 91.5)
                            lPieBlast.mState = PieBlast.States.FiredLeftBarrel;
                        break;

                    case PieBlast.States.FiredLeftBarrel:
                        lPieBlast.mElapsedTime += lDelta;
                        lNewOrientation = lPieBlast.orientation * Matrix.CreateTranslation(lPieBlast.orientation.Backward * lPieBlast.mSpeed * lDelta);
                        BoundingSphere BS = new BoundingSphere(lPieBlast.orientation.Translation, 10);

                        //Tests for Pie collision with alien
                        if (mGame.Alien.BoundingCyl.Intersect(BS))
                        {
                            lPieBlast.mState = PieBlast.States.Attached;
                            mGame.Alien.State = AlienModel.States.Eat;
                            mAlienPie = lPieBlast;
                            mPieBlasts.Remove(lPieBlast);
                            i--;
                        }
                        //Tests for pie collision with dalek
                        else if (mGame.DalekModel.BoundingCyl.Intersect(BS))
                        {
                            lPieBlast.mState = PieBlast.States.Attached;
                            mDalekPie = lPieBlast;
                            mPieBlasts.Remove(lPieBlast);
                            i--;
                        }
                        // Tests for pie collision with Gort
                        else if ( mGame.GortModel.BoundingCyl.Intersect(BS))
                        {
                            lPieBlast.mState = PieBlast.States.Attached;
                            mGortPie = lPieBlast;
                            mPieBlasts.Remove(lPieBlast);
                        }
                        //Tests to see if the pie still hasn't hit anything
                        else if ( !mGame.Victoria.TestRegion(lNewOrientation.Translation).StartsWith("W_") &&
                                  !mGame.Victoria.TestRegion(lNewOrientation.Translation).StartsWith("R_Door"))
                        {
                            lPieBlast.orientation = lNewOrientation;
                        }
                        else
                        {
                            lPieBlast.mState = PieBlast.States.Collision;
                        }
                        
                        break;

                    case PieBlast.States.Attached:

                        break;

                    case PieBlast.States.Collision:

                        //Do nothing
                        break;

                }
            }
            /*// Loop over all the PieBlasts that we have
            for (LinkedListNode<PieBlast> lPieBlastNode = mPieBlasts.First; lPieBlastNode != null; )
            {
                LinkedListNode<PieBlast> lNextBlast = lPieBlastNode.Next;

                // What to do if the pie has hit the wall
                if (lPieBlastNode.Value.mWallHit)
                {

                    continue;
                }
                //PieBlast lNewB


                lPieBlastNode = lNextBlast;
            }*/
        }

        /// <summary>
        /// This function is called to draw this game component.
        /// </summary>
        /// <param name="aGraphics"></param>
        /// <param name="aGameTime"></param>
        public void Draw(GraphicsDeviceManager aGraphics, GameTime aGameTime, Matrix aBazookaLocation)
        {
            foreach (PieBlast lBlast in mPieBlasts)
            {
                DrawModel(aGraphics, mPieModel, lBlast.orientation, lBlast);
            }

            if (mDalekPie != null)
                DrawModel(aGraphics, mPieModel, mDalekPie.orientation, mDalekPie);

            if (mGortPie != null)
                DrawModel(aGraphics, mPieModel, mGortPie.orientation, mGortPie);
            if (mAlienPie != null)
                DrawModel(aGraphics, mPieModel, mAlienPie.orientation, mAlienPie);
            
        }

        /// <summary>
        /// Draws the pie blast models
        /// </summary>
        /// <param name="aGraphics"></param>
        /// <param name="aModel"></param>
        /// <param name="aBazookaLocation"></param>
        private void DrawModel(GraphicsDeviceManager aGraphics, Model aModel, Matrix aBazookaLocation, PieBlast aPie)
        {
            Matrix[] lTransforms = new Matrix[aModel.Bones.Count];
            aModel.CopyAbsoluteBoneTransformsTo(lTransforms);

            foreach (ModelMesh lMesh in aPie.mMesh)
            {
                foreach (Effect lEffect in lMesh.Effects)
                {
                    if (lEffect is BasicEffect)
                    {
                        BasicEffect lBasicEffect = lEffect as BasicEffect;
                        lBasicEffect.EnableDefaultLighting();
                        lBasicEffect.World = aBazookaLocation;
                        lBasicEffect.View = mGame.Camera.View;
                        lBasicEffect.Projection = mGame.Camera.Projection;

                    }
                    else
                    {
                        lEffect.Parameters["World"].SetValue( aBazookaLocation);
                        lEffect.Parameters["View"].SetValue(mGame.Camera.View);
                        lEffect.Parameters["Projection"].SetValue(mGame.Camera.Projection);

                        lEffect.Parameters["Weight"].SetValue(mGame.SlimeWeight);
                    }
                }
                lMesh.Draw();
            }
        }

        public void FirePie()
        {
            
            // We are not out of pies to fire
            if (mBulletsLeft > 0)
            {
                //Transitions the loaded pie to the firing state
                mPieBlasts[mPieBlasts.Count - 1].mState = PieBlast.States.FiredStillInBarrel;

                mBulletsLeft--;

                //Adds the next pie blast to be shot

                if (mBulletsLeft > 0)
                {
                    PieBlast mNewBlast = new PieBlast();
                    mNewBlast.mPieNumber = mCurrentPie;
                    mNewBlast.orientation = mGame.Victoria.BazMat;
                    mNewBlast.mMesh = mPieModels[mCurrentPie++ % 3];

                    mPieBlasts.Add(mNewBlast);
                }
            }
        }

        public void Reload()
        {
            mBulletsLeft = 10;

            PieBlast lPie = new PieBlast();
            lPie.orientation = mGame.Victoria.BazMat;
            lPie.mMesh = mPieModels[mCurrentPie++ % 3];
            mPieBlasts.Add(lPie);
        }

        public bool IncomingPie(BoundingCylinder aBC, ref Vector3 aInter)
        {
            foreach (PieBlast lBlast in mPieBlasts)
            {
                Ray lRay = new Ray(lBlast.orientation.Translation, lBlast.orientation.Backward);
                if (lBlast.mState == PieBlast.States.FiredLeftBarrel && aBC.Intersect(lRay))
                {
                    aInter = lBlast.orientation.Translation;
                    return true;
                }
            }
            return false;
        }
    }
}
